/*
 * Copyright 2015 泛泛o0之辈
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.jfast.framework.web.api;

import cn.jfast.framework.jdbc.annotation.NoAutoLog;
import cn.jfast.framework.jdbc.db.ConnectionFactory;
import cn.jfast.framework.log.LogFactory;
import cn.jfast.framework.log.LogType;
import cn.jfast.framework.log.Logger;
import cn.jfast.framework.upload.UploadFile;
import cn.jfast.framework.web.aop.AopHandler;
import cn.jfast.framework.web.view.View;
import cn.jfast.framework.web.view.ViewDriver;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.*;

/**
 * Api动作流程控制
 */
public class ApiInvocation {

	/** 被访问Api类对象 */
	private final Object targetApi;

	private Logger log = LogFactory.getLogger(LogType.JFast,
			ApiLogger.class);
	/** 拦截器链 */
	private final List<AopHandler> aopList;
	/** 包装后的Api对象 */
	private final Api api;
	/** http请求 */
	private final HttpRequest request;
	/** http响应 */
	private final HttpResponse response;
	/** Api返回视图 */
	private Object view;
	/** 视图驱动器 */
	private final ViewDriver viewDriver;
	/** Api方法反射类 */
	private final ApiInvoker invoker;
	/** Api执行链游标 */
	private int cur = 0;
	/** 执行长度标志 */
	private int STACK_SIZE = 0;
	/** Api路径 */
	private String route;

	private boolean isErrored = false;
	/** 异常记录 */
	private List<Exception> ex = new ArrayList<Exception>();

	private HttpRequest apiRequest;

	/** 拦截器后置方法中可以获取Api返回视图 */
	public Object getView() {
		return view;
	}

	public void assignApiParam(String attrName, Object attrValue) {
		if(null  == api.getMethod().getAnnotation(NoAutoLog.class))
			log.info("\n"+
					 "%s	:	------------------ 参数信息 ------------------\n" +
		             "%s	:	%s \n"+
		             "%s	:	%s \n ",
		             "参数变更",
		             "参数名称", attrName,
		             "注入参数", attrValue);
		this.invoker.addAopScopeParam(attrName, attrValue);
	}

	public String getParameter(String paramName) {
		String value = this.apiRequest.getParameter(paramName);
		if (null == value)
			value = invoker.getParameter(paramName);
		if(null == value)
			value = request.getParameter(paramName);
		return value;
	}

	public String[] getParameters(String paramName) {
		return this.apiRequest.getParameters(paramName);
	}

	public List<UploadFile> getUploadFile(String paramName) {
		return this.apiRequest.getUploadFile(paramName);
	}

	public ApiInvocation(Api api, Object object, HttpRequest request,
			HttpResponse response, String route) {
		this.api = api;
		this.targetApi = ApiContext.fillResource(object);
		this.aopList = api.getAopList();
		this.STACK_SIZE = this.aopList.size();
		this.request = request;
		this.response = response;
		this.viewDriver = new ViewDriver();
		this.route = route;
		invoker = new ApiInvoker();
		invoker.setApiMethod(api.getMethod());
		invoker.setApi(targetApi);
		invoker.setApiClass(api.getApi());
		invoker.setRequest(request);
		invoker.setResponse(response);
		invoker.setReqRoute(route);
		invoker.setApiRoute(api.getApiUri());
		for (int i = 0; i < aopList.size(); i++) {
			aopList.set(i, (AopHandler) ApiContext.fillResource(aopList.get(i)));
		}
		this.apiRequest = invoker.getRequest();
		invoker.init();
		invoker.setInvocation(this);
	}
	
	public String getAopNames(){
		String[] aopNames = new String[aopList.size()];
		for(int i=0; i < aopList.size(); i++){
			aopNames[i] = aopList.get(i).getClass().getName();
		}
		return Arrays.toString(aopNames);
	}
	
	public Object getBean() {
		return targetApi;
	}

	public Method getMethod() {
		return api.getMethod();
	}

	public String getRoute() {
		return route;
	}

	protected void addException(Exception e) {
		this.ex.add(e);
	}

	public synchronized void invoke() {
		if (!isErrored) {
			try {
				if(cur == 0){
					invoker.preLog();
				}
				if (cur < STACK_SIZE) {
					aopList.get(cur++).beforeHandle(this, request, response, ex);
				} else if (cur == STACK_SIZE) {
					cur++;
					view = invoker.invoke();
					invoke();
				} else if (cur <= 2 * STACK_SIZE) {
					aopList.get(2 * STACK_SIZE - cur++).afterHandle(this,
							request, response, ex);
				}
			} catch (Exception ex) {
				this.ex.add(ex);
				invoke();
			} finally {
				if (cur == 2 * STACK_SIZE + 1 || !ex.isEmpty()) {
					cur = Integer.MAX_VALUE;
					try {
						invoker.preLog(); // 打印前日志
						viewDriver.render(view, request, response, ex); // 渲染视图，打印错误日志
						invoker.afterLog(view); // 打印后日志
					} catch (Exception e) {
						log.error(Thread.currentThread().toString(), e);
					} finally {
					}
				}
			}
		}
	}

	public synchronized void renderException(View view) {
		if (!isErrored) { // 保证renderException只执行一次
			isErrored = true;
			this.view = view;
			cur = Integer.MAX_VALUE;
			try {
				Connection conn = ConnectionFactory.getThreadLocalConnection();
				if(!conn.isClosed()){
					if(conn.getAutoCommit() == false)
						conn.rollback();
					conn.close();
				}
				invoker.preLog(); // 打印前日志
				for (Exception e : ex) {// 打印错误日志
					log.error(Thread.currentThread().toString(), e);
				}
				invoker.afterLog(view); // 打印后日志
			} catch (Exception e) {
				log.error(Thread.currentThread().toString(), e);
				invoker.afterLog(view); // 打印后日志
			}
			this.ex = new ArrayList<Exception>();
			viewDriver.render(view, request, response, ex); // 只渲染视图,不再打印错误日志
		}
	}

}
